"""
认证服务 - 符合用友云开放平台规范
"""
import uuid
import hmac
import hashlib
import base64
import logging
from datetime import datetime, timedelta
from urllib.parse import quote, unquote
from config import get_config

logger = logging.getLogger(__name__)
config = get_config()

# Token存储（内存）- {access_token: {username, appKey, expires_at, issued_at}}
token_store = {}

# 应用配置（模拟appKey和appSecret）
# 支持从配置文件加载，也支持多应用
def _load_app_credentials():
    """从配置加载应用凭证"""
    credentials = {
        # 测试应用（默认保留用于测试）
        'test_app_key': {
            'appKey': 'test_app_key',
            'appSecret': 'test_app_secret',
            'appName': '测试应用',
            'tenantId': 'tenant_test_001'
        }
    }

    # 从配置文件加载主应用
    if config.APP_KEY and config.APP_SECRET:
        credentials[config.APP_KEY] = {
            'appKey': config.APP_KEY,
            'appSecret': config.APP_SECRET,
            'appName': config.APP_NAME,
            'tenantId': config.TENANT_ID
        }

    return credentials

APP_CREDENTIALS = _load_app_credentials()


class AuthService:
    """认证服务类 - 用友云标准"""

    @staticmethod
    def calculate_signature(app_secret: str, param_map: dict) -> str:
        """
        计算签名 - 用友云标准
        URLEncode(Base64(HmacSHA256(parameterMap)))

        参数按名称排序，依次拼接参数名和参数值（signature字段除外）
        """
        # 排序参数（排除signature）
        sorted_params = sorted(
            [(k, v) for k, v in param_map.items() if k != 'signature'],
            key=lambda x: x[0]
        )

        # 拼接参数：paramNameparamValueparamName2paramValue2...
        param_string = ''.join([f"{k}{v}" for k, v in sorted_params])

        # HmacSHA256加密
        hmac_obj = hmac.new(
            app_secret.encode('utf-8'),
            param_string.encode('utf-8'),
            hashlib.sha256
        )
        signature_bytes = hmac_obj.digest()

        # Base64编码
        base64_signature = base64.b64encode(signature_bytes).decode('utf-8')

        # URL编码
        url_encoded_signature = quote(base64_signature, safe='')

        return url_encoded_signature

    @staticmethod
    def verify_signature(app_key: str, timestamp: int, signature: str) -> bool:
        """验证签名"""
        # 获取应用配置
        app_config = APP_CREDENTIALS.get(app_key)
        if not app_config:
            logger.warning(f"[签名验证] appKey不存在: {app_key}")
            logger.warning(f"[签名验证] 当前支持的appKey: {list(APP_CREDENTIALS.keys())}")
            return False

        # 重新计算签名
        param_map = {
            'appKey': app_key,
            'timestamp': str(timestamp)
        }

        calculated_signature_encoded = AuthService.calculate_signature(
            app_config['appSecret'],
            param_map
        )
        calculated_signature_base64 = unquote(calculated_signature_encoded)

        # 对比签名（兼容传入为URL编码或Base64原文）
        provided_signature = signature or ""
        is_valid = (
            provided_signature == calculated_signature_encoded or
            provided_signature == calculated_signature_base64
        )

        if is_valid:
            logger.info(f"[签名验证] ✅ 签名验证成功 - appKey: {app_key}")
        else:
            logger.warning(f"[签名验证] ❌ 签名验证失败 - appKey: {app_key}")
            logger.warning(f"[签名验证] 客户端签名: {signature[:30]}...")
            logger.warning(f"[签名验证] 服务端计算(URLEncoded): {calculated_signature_encoded[:30]}...")
            logger.warning(f"[签名验证] 服务端计算(Base64): {calculated_signature_base64[:30]}...")
            logger.warning(f"[签名验证] APP_SECRET: {app_config['appSecret'][:10]}...")
            logger.warning(f"[签名验证] 时间戳: {timestamp}")

        return is_valid

    @staticmethod
    def get_access_token(app_key: str, timestamp: int, signature: str) -> dict:
        """
        获取access_token - 用友云标准接口

        对应接口：/iuap-api-auth/open-auth/selfAppAuth/base/v1/getAccessToken
        """
        from config import get_config
        config = get_config()

        # 验证appKey是否存在
        if app_key not in APP_CREDENTIALS:
            return {
                "code": "40001",
                "message": "appKey不存在或无效"
            }

        # 验证时间戳（防止重放攻击，时间戳不能超过5分钟）
        current_time = int(datetime.now().timestamp() * 1000)
        time_diff = abs(current_time - timestamp)
        if time_diff > 5 * 60 * 1000:  # 5分钟
            return {
                "code": "40002",
                "message": "时间戳无效，请求已过期"
            }

        # 验证签名（测试模式下可跳过）
        if not config.SKIP_SIGNATURE_VERIFY:
            if not AuthService.verify_signature(app_key, timestamp, signature):
                return {
                    "code": "40003",
                    "message": "签名验证失败"
                }
        else:
            # 测试模式：跳过签名验证
            logger.info(f"[测试模式] 跳过签名验证: appKey={app_key}")

        # 生成access_token（用友云格式：长Base64字符串）
        # 模拟用友云的token格式：随机生成一个长token
        import base64
        import random
        import string

        # 生成类似用友云的长token
        random_bytes = ''.join(random.choices(string.ascii_letters + string.digits, k=80))
        access_token = base64.b64encode(random_bytes.encode()).decode()
        # 添加一些随机后缀，使其更像用友云的token
        access_token = access_token + ''.join(random.choices(string.ascii_letters + string.digits, k=40))

        # Token有效期：从配置读取，默认2小时（7200秒）
        expire_seconds = config.TOKEN_EXPIRE_SECONDS
        expires_at = datetime.now() + timedelta(seconds=expire_seconds)

        # 存储token信息
        token_store[access_token] = {
            'appKey': app_key,
            'tenantId': APP_CREDENTIALS[app_key]['tenantId'],
            'expires_at': expires_at,
            'issued_at': datetime.now()
        }

        # 返回格式完全匹配用友云（注意：expire在前，access_token在后）
        return {
            "code": "00000",
            "message": "成功！",
            "data": {
                "expire": expire_seconds,
                "access_token": access_token
            }
        }

    @staticmethod
    def generate_token(username: str) -> dict:
        """
        兼容旧版本的token生成方法
        用于测试，实际应使用get_access_token
        """
        import base64
        import random
        import string
        from config import get_config

        # 生成类似用友云的长token
        random_bytes = ''.join(random.choices(string.ascii_letters + string.digits, k=80))
        access_token = base64.b64encode(random_bytes.encode()).decode()
        access_token = access_token + ''.join(random.choices(string.ascii_letters + string.digits, k=40))

        config = get_config()
        expire_seconds = config.TOKEN_EXPIRE_SECONDS
        expires_at = datetime.now() + timedelta(seconds=expire_seconds)

        token_store[access_token] = {
            'username': username,
            'appKey': 'test_app_key',
            'tenantId': 'tenant_test_001',
            'expires_at': expires_at,
            'issued_at': datetime.now()
        }

        # 返回用友云标准格式
        return {
            "expire": expire_seconds,
            "access_token": access_token
        }

    @staticmethod
    def validate_token(token: str) -> bool:
        """验证token是否有效"""
        if not token:
            logger.warning("[Token验证] Token为空")
            return False

        # 检查token是否存在
        token_info = token_store.get(token)
        if not token_info:
            logger.warning(f"[Token验证] Token不存在于store中: {token[:20]}...")
            logger.warning(f"[Token验证] 当前store中有 {len(token_store)} 个token")
            if len(token_store) > 0:
                # 显示store中的token前缀（用于对比）
                stored_tokens = list(token_store.keys())
                logger.warning(f"[Token验证] Store中的token前缀: {[t[:20] + '...' for t in stored_tokens[:3]]}")
            return False

        # 检查是否过期
        if datetime.now() > token_info['expires_at']:
            logger.warning(f"[Token验证] Token已过期: 过期时间={token_info['expires_at']}, 当前时间={datetime.now()}")
            # 清除过期token
            token_store.pop(token, None)
            return False

        logger.info(f"[Token验证] Token有效: appKey={token_info.get('appKey')}, 过期时间={token_info['expires_at']}")
        return True

    @staticmethod
    def cleanup_expired_tokens():
        """清理过期的token"""
        expired_tokens = [
            token for token, info in token_store.items()
            if datetime.now() > info['expires_at']
        ]
        for token in expired_tokens:
            token_store.pop(token, None)
        return len(expired_tokens)

    @staticmethod
    def get_app_list() -> list:
        """获取应用列表（用于管理界面）"""
        return [
            {
                'appKey': config['appKey'],
                'appName': config['appName'],
                'tenantId': config['tenantId']
            }
            for config in APP_CREDENTIALS.values()
        ]
